﻿// Socket.cpp : 实现文件
//

#include "stdafx.h"
//#include "GHCY.h"
#include "../Hui/Hui.h"
//#include "CarPLC.h"
//#include "GException.h"
//#include "LogList.h"
//#include <atlconv.h>
#include "Socket.h"

// Socket

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif


// 参考：
// OMRON PLC 以态网模块手册 w421-e1-04_cs1w_cj1w-etn21.pdf，FINS/TCP Method P186
// 返回错误码，OMRON PLC 以态网模块手册 w421-e1-04_cs1w_cj1w-etn21.pdf，FINS/TCP Method P120

UINT Socket::threadPro(LPVOID pParam) // 线程处理函数，LPVOID pParam 指向本类
{
	return ((Socket *)pParam)->threadLoop();
}

// 参考：
// OMRON PLC 以态网模块手册 w421-e1-04_cs1w_cj1w-etn21.pdf，FINS/TCP Method P186

// 不能在线程中使用，收不到OnConnect等消息
// CAsyncSocket已经创建线程，不用再加线程
Socket::Socket( LPCTSTR ip, int port )
	: m_nSID(0)
	//, m_bSent( FALSE )
	//, m_bKill( FALSE )
	, m_socket( INVALID_SOCKET )
	, m_nTick( 0 )
	, m_strIP( ip )
	, m_nPort( port )
	, m_pThread( 0 )
	, m_nFinsNode( 0 )
	, m_bTimeOut(FALSE)
{
	m_strIPPort.Format( L"%s:%i", (LPCTSTR)m_strIP, m_nPort );

	int fin = m_strIP.ReverseFind( '.' );
	if( fin >= 0 )
	{
		CString strFinsNode = (LPCTSTR)m_strIP + fin + 1;
		m_nFinsNode = (BYTE)_wtoi( strFinsNode );
	}
}

Socket::~Socket()
{
	//	if (m_pSocket->m_hSocket != INVALID_SOCKET)
	//{
	//	m_pSocket->Close();
	//}
}


// Socket 成员函数

// 20170321 改用UDP，因为连接不稳定
// 连接，PLC是服务端，上位机是客户端，LPCTSTR lpszHostAddress PLC地址, UINT nHostPort PLC端口。
// http://www.cnblogs.com/ayanmw/archive/2012/05/03/2480854.html
// http://bbs.csdn.net/topics/392069164
// http://blog.csdn.net/tuan891205/article/details/8516185
// http://blog.csdn.net/liujiayu2/article/details/51726041
BOOL Socket::connect()
{
	//if( m_bKill )	// 再判断一次，因为执行上一句时，这个值可能被主进程改变
	//	return FALSE;

	//PCTSTR lpszHostAddress = m_pCar->m_strIP;	// IP地址
	//int nHostPort = m_pCar->m_nPort;			// 端口

	//创建UDP socket，!! 需要先启用WSAStartup !!
	m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);	// udp方式创建SOCKET
	if (m_socket == INVALID_SOCKET)
	{
		// 保存日志
		//GException ge;
		//ge.m_nCode		= 2028;
		//ge.m_strObject	= m_strIPPort;
		//ge.m_strSummary	= L"socket() 错误。";
		//ge.m_strDetails.Format( L"错误代码：%i, 详见 https://msdn.microsoft.com/en-us/library/ms740668(v=vs.85).aspx"
		//	, WSAGetLastError() );

		//theApp.m_pLogList->addLog( &ge );

		Sleep(2000);  // 不成功时加2秒延时，否则日志更新太快
		
		//m_pCar->setPos( 0, -28 );	// -28 建立SOCKET错误

		onScoketError();

		return FALSE;
	}

	//if( m_bKill )	// 再判断一次，因为执行上一句时，这个值可能被主进程改变
	//	return FALSE;

	//本地网卡IP和端口绑定，否则随机分配地址，多次连接会阻塞
	// https://blog.csdn.net/CharlesJhonson/article/details/105450420
	// 1 - 1023：BSD保留端口。
	// 1024 - 5000：BSD临时端口
	// 5001 - 65535：BSD服务器（非特权）
	// https://www.dyxmq.cn/program/code/c-cpp/bind-random-port-for-a-socket.html
	SOCKADDR_IN client_addr;
	client_addr.sin_family = AF_INET;
	client_addr.sin_addr.s_addr = htonl(INADDR_ANY);		// 任意本地地址
	client_addr.sin_port = htons(10000);		// 指定端口，否则随机分配地址，多次连接会阻塞

	//下面这一步是绑定本地网卡IP，返回值为0，加快连接速度
	if (bind(m_socket, (sockaddr*)&client_addr, sizeof(client_addr)) == SOCKET_ERROR)
	{

	}

	// PLC地址
	USES_CONVERSION;
	memset( &m_addPLC, 0, sizeof(m_addPLC) );
	m_addPLC.sin_family			= AF_INET;
	//m_addPLC.sin_addr.s_addr	= inet_addr( T2A(m_strIP) );	//inet_addr 将被弃用
	InetPton(AF_INET, m_strIP, &m_addPLC.sin_addr.s_addr);	//https://baike.baidu.com/item/inet_pton/9555884?fr=aladdin
	m_addPLC.sin_port			= htons( u_short(m_nPort) );

		//set Recv and Send time out
	int nTransmitTimeout = int( 3 * 1000 );
	int iResult = setsockopt( m_socket,SOL_SOCKET, SO_SNDTIMEO, (char *)&nTransmitTimeout, sizeof(nTransmitTimeout) );
	if (iResult == SOCKET_ERROR) {
		// 保存日志
		//GException ge;
		//ge.m_nCode		= 2032;
		//ge.m_strObject	= m_strIPPort;
		//ge.m_strSummary	= L"setsockopt() 设置发送超时错误。";
		//ge.m_strDetails.Format( L"错误代码：%i, 详见 https://msdn.microsoft.com/en-us/library/ms740668(v=vs.85).aspx"
		//	, WSAGetLastError() );

		//theApp.m_pLogList->addLog( &ge );

		//m_pCar->setPos( 0, -32 );	// -32 setsockopt() 设置发送超时错误。

		onScoketError();

		return FALSE;
	}

	//if( m_bKill )	// 再判断一次，因为执行上一句时，这个值可能被主进程改变
	//	return FALSE;

	iResult = setsockopt( m_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&nTransmitTimeout, sizeof(nTransmitTimeout) );
	if (iResult == SOCKET_ERROR) {
		// 保存日志
		//GException ge;
		//ge.m_nCode		= 2033;
		//ge.m_strObject	= m_strIPPort;
		//ge.m_strSummary	= L"setsockopt() 设置接收超时错误。";
		//ge.m_strDetails.Format( L"错误代码：%i, 详见 https://msdn.microsoft.com/en-us/library/ms740668(v=vs.85).aspx"
		//	, WSAGetLastError() );

		//theApp.m_pLogList->addLog( &ge );

		//m_pCar->setPos( 0, -33 );	// -33 setsockopt() 设置接收超时错误。

		onScoketError();

		return FALSE;
	}

	m_bTimeOut = FALSE;
	return TRUE;
}

// 发送数据, LPCSTR pBuf 数据缓冲, int len 数据长度
BOOL Socket::sendData( PCHAR pBuf, int len )
{
	static int t1 = 0, t2 = 0, t3 = 0;
	t2 = GetTickCount();
	int r = sendto( m_socket, pBuf, len, 0, (sockaddr *)&m_addPLC, sizeof(m_addPLC) );
	t3 = t2 - t1;
	t1 = t2;

	if (r == SOCKET_ERROR) {
		// 保存日志
		//GException ge;
		//ge.m_nCode		= 2009;
		//ge.m_strObject	= m_strIPPort;
		//ge.m_strSummary	= L"send() 发送数据错误";

		//CString & str = ge.m_strDetails;
		//str = L"发送数据";
		//for( int i = 0; i < len; i++ )
		//{
		//	PCTSTR s = i & 0xf ? i & 3 ? L" %02X" : L"  %02X" : L"\r\n%02X";
		//	str.AppendFormat( s, pBuf[i] );
		//}

		//theApp.m_pLogList->addLog( &ge );

		//m_pCar->setPos( 0, -9 );	// send() 发送数据错误。

		onScoketError();

		return FALSE;
    }
	else if( r != len ) {
		// 保存日志
		//GException ge;
		//ge.m_nCode		= 2008;
		//ge.m_strObject	= m_strIPPort;	// IP地址;
		//ge.m_strSummary	= L"send() 发送数据长度错误，0长度为超时。";
		//ge.m_strDetails.Format( L"发送长度：%i, 正确长度：%i"
		//	, r, len );

		//theApp.m_pLogList->addLog( &ge );
		
		//m_pCar->setPos( 0, -8 );	// send() 发送数据长度错误。

		onScoketError();

		return FALSE;
    }

	return TRUE;
}

// 接收数据, int len 数据长度，接收数据保存到m_pRecBuf中

int Socket::receivePLC()
{
	//ASSERT( len < sizeof( m_pRecBuf ) );

	int addrlen = sizeof(m_addPLC);
	int r = recvfrom( m_socket, (PSTR)m_pRecBuf, sizeof( m_pRecBuf ), 0, (sockaddr *)&m_addPLC, &addrlen );

	if( r == SOCKET_ERROR )
	{
		//// 保存日志
		//GException ge;
		//ge.m_nCode		= 2005;
		//ge.m_strObject	= m_strIPPort;	// IP地址;
		//ge.m_strSummary	= L"receivePLC() 接收数据错误。";
		//ge.m_strDetails.Format( L"错误代码：%i, 详见 https://msdn.microsoft.com/en-us/library/ms740668(v=vs.85).aspx"
		//	, WSAGetLastError() );

		//theApp.m_pLogList->addLog( &ge );

		onScoketError();

		return 0;
	}

	return r;
}

// 线程中循环传输
BOOL Socket::threadLoop()
{
	int t1 = GetTickCount(), t2 = 0;

	BOOL r = connect();

	while (1 /*&& !m_bKill*/)
	{
		//t1 = GetTickCount();

		//if (theApp.m_mutexExit.isLocked())	//正在退出，中断
		//	break;

		int r = receivePLC();

		Sleep(43);

		if (!r)
			continue;

		{
			t2 = GetTickCount();
			m_nTick = t2 - t1;
			t1 = t2;

			// 与主线程互斥，操作共享内存
			//mutexBuf.Lock();
			//memcpy( m_pSharedBuf, m_pRecBuf, BUFLENGTH );
			//mutexBuf.Unlock();

			//if (theApp.m_mutexExit.isLocked())	//正在退出，中断
			//	break;

			//onRead();


	// 回调函数 https://blog.csdn.net/fairytailq/article/details/78376414#:~:text=MFC%E4%B8%AD%E5%BA%94%E8%AF%A5%E6%9C%89%E4%B8%A4%E7%B1%BB%E5%9B%9E
			ASSERT(pCallBack);
			//if(pCallBack)
			pCallBack(m_pRecBuf, r);
		}
	}

	return 0;
}

// 执行收发任务
BOOL Socket::transmitRead()
{
	// 发送读数据命令
	m_pReadData[9] = ++m_nSID;	// SID
	// 46 49 4e 53 00 00 00 1a 00 00 00 02 00 00 00 00 80 00 02 00 01
	// 00 00 fb 00 01 01 01 b2 00 00 00 00 14
	// 详细格式见Socket::Socket( Car * pCar )
	//if( m_bKill )	// 再判断一次，因为执行上一句时，这个值可能被主进程改变
	//	return FALSE;

	if(!transmit((PCHAR)m_pReadData, 100, 100))
		return FALSE;

	//|| (*PWORD(m_pRecBuf + 26) != 0x0101)	// 从PLC读数据标识
	if(    (m_pReadData[3] != m_pRecBuf[6])
		|| (m_pReadData[4] != m_pRecBuf[7])
		|| (m_pReadData[5] != m_pRecBuf[8])
		//|| (m_pReadData[9]		!= m_pRecBuf[9])	// sid	//?? 20160105 不判断SID, 改在receivePLC中调正
		)
	{
		// 保存日志
		//GException ge;
		//ge.m_nCode		= 2036;	// receivePLC() 接收数据格式错误
		//ge.m_strObject	= m_strIPPort;
		//ge.m_strSummary	= L"receivePLC() 接收数据格式错误";

		//CString & str = ge.m_strDetails;
		//str = L"接收数据";
		//for( int i = 0; i < HEADNUM; i++ )
		//{
		//	PCTSTR s = i & 0xf ? i & 3 ? L" %02X" : L"  %02X" : L"\r\n%02X";
		//	str.AppendFormat( s, m_pRecBuf[i] );
		//}

		//theApp.m_pLogList->addLog( &ge );
		
		//m_pCar->setPos( 0, -36 );	// receivePLC() 接收数据头错误。

		onScoketError();

		return FALSE;
	}
	
	return TRUE;
}

/// <类型>Fanction</类型>
/// <作者>Mr. Hu</作者>
/// <日期>20200315</日期>
/// <名称>transmit</名称>
/// <功能>发送和接收PLC数据</功能>
/// <参数 name = "pSendBuf">发送缓存</参数>
/// <参数 name = "nSendLen">发送长度</参数>
/// <参数 name = "nReadLen">接收长度</参数>
/// <返回>TURN 成功，FALSE 失败</返回>
/// <说明>发送和接收试10次，约2秒</说明>
BOOL Socket::transmit(PCHAR pSendBuf, int nSendLen, int nReadLen)
{
	int ii = 10;	// 传送10次
	for (; ii > 0; ii--)
	{
		//if (theApp.m_mutexExit.isLocked())	//正在退出，中断
		//	return FALSE;

		//发送取数据命令
		if (!sendData(pSendBuf, nSendLen))
		{
			Sleep(100);		// 发送不成功，等100ms继续发
			continue;
		}

		//if (theApp.m_mutexExit.isLocked())	//正在退出，中断
		//	return FALSE;

		//接送数据
		if (!receivePLC())
			continue;

		break;
	}

	if (!ii)
	{
		// 保存日志
		//GException ge;
		//ge.m_nCode = 2005;
		//ge.m_strObject = m_strIPPort;	// IP地址;
		//ge.m_strSummary = L"Socket::transmit() 传送数据错误。";
		//ge.m_strDetails.Format(L"错误代码：%i, 详见 https://msdn.microsoft.com/en-us/library/ms740668(v=vs.85).aspx"
		//	, WSAGetLastError());

		//theApp.m_pLogList->addLog(&ge);

		onScoketError();

		m_bTimeOut = TRUE;		// 置通讯超时

		return FALSE;
	}
	m_bTimeOut = FALSE;		// 清通讯超时

	return TRUE;
}

// 向下位机写数据
BOOL Socket::transmitSend()
{
		//消息队列必须有数据
	ASSERT( m_sendQueue.nCount > 0 );

	// 从队列取要发送的数据
	//m_sendQueue.shift( m_pSendData, TOTALSEND );
	m_sendQueue.getFirst( m_pSendData, 100 );

	// 从发送数据中也发送长度，
	int nn = 0;
	GETBYTE(nn, 0) = m_pSendData[17];	// 把PLC数据转换成C数据
	GETBYTE(nn, 1) = m_pSendData[16];

	// 发送数据总长度
	int len = m_pSendData[12] & 0x80	// 内存区标志的最高位为1表示按字（两字节）传送，否则按二制位传送，每个二进制位占一个字节
		? 100 + nn * 2
		: 100 + nn;
		
	// 传送标记
	m_pSendData[9] = ++m_nSID;		 // SID
	// 46 49 4e 53 00 00 00 1a 00 00 00 02 00 00 00 00 80 00 02 00 01
	// 00 00 fb 00 01 01 01 b2 00 00 00 00 14
	// 详细格式见Socket::Socket( Car * pCar )

	//if( m_bKill )	// 再判断一次，因为执行上一句时，这个值可能被主进程改变
	//	return FALSE;

	if(!transmit((PCHAR)m_pSendData, len, 100))
		return FALSE;

	m_sendQueue.removeFirst();		// 接收到反馈后删除第一条数据，//?? 是不是应该放到后面? 会不会发送成功了但是没有反馈而又多发一条。

	if( (m_pSendData[3] != m_pRecBuf[6])
		|| (m_pSendData[4] != m_pRecBuf[7])
		|| (m_pSendData[5] != m_pRecBuf[8])
		//|| (m_pSendData[9] == m_pRecBuf[9])	// sid	//?? 20160105 不判断SID, 改在receivePLC中调正
		)
	{
		// 46 49 4e 53  00 00 00 16
		// 00 00 00 02  00 00 00 00
		// c0 00 02 00  fb 00 00 f1
		// 00 38 01 02  00 00

		// 保存日志
		//GException ge;
		//ge.m_nCode		= 2039;	// receivePLC() 接收数据格式错误2
		//ge.m_strObject	= m_strIPPort;
		//ge.m_strSummary	= L"receivePLC() 接收数据格式错误2";

		//CString & str = ge.m_strDetails;
		//str = L"接收数据2";
		//for( int i = 0; i < HEADNUM; i++ )
		//{
		//	PCTSTR s = i & 0xf ? i & 3 ? L" %02X" : L"  %02X" : L"\r\n%02X";
		//	str.AppendFormat( s, m_pRecBuf[i] );
		//}

		//theApp.m_pLogList->addLog( &ge );

		onScoketError();

		//m_pCar->setPos( 0, -36 );	// receivePLC() 接收数据头错误。

		return FALSE;
	}
	
	return TRUE;
}

// 结束通讯线程，这段程序代码在Socket.cpp中，由主线程调用
void Socket::killThread()
{
	//if( theApp.m_pLogList )
	//	theApp.m_pLogList->criticalLog.Lock();	// 防止线程正在写日志

	//mutexExit.Lock();	// 线程互斥，防止通讯工作中杀死线程

	// 先关闭连接
	if (m_socket != INVALID_SOCKET)
	{
		closesocket( m_socket );
		m_socket = INVALID_SOCKET;
		//Sleep( 50 );	// ?? 加个延时，不然退出时出错或死机
	}

	// 强制结束线程
	if( m_pThread )
	{
		m_pThread->m_bAutoDelete = TRUE;
		//DWORD ExitCode;
		//GetExitCodeThread(m_pThread->m_hThread, &ExitCode);
		//TerminateThread(m_pThread->m_hThread, ExitCode);
		WaitForSingleObject(m_pThread->m_hThread, 500);		// 等待线程结束

		//Sleep(50);	// ?? 加个延时，不然退出时出错或死机

		//delete m_pThread;
	}

	//mutexExit.Unlock();

	//if( theApp.m_pLogList )
	//	theApp.m_pLogList->criticalLog.Unlock();	// 防止线程正在写日志

	//m_bKill = TRUE;	// 结束线程循环

	// DEBUG状态下长期等待，发布状态等待500ms后强制结束
//#ifdef _DEBUG
//	WaitForSingleObject(m_hEventKilled, INFINITE);
//#else
//	WaitForSingleObject(m_hEventKilled, 1000);
//#endif
}

/// <说明>发送一位二进制数据到PLC</说明>
void Socket::sendBitByBit(int nPLCAdd, WORD nPile, WORD nValue)
{
	if (!nPile)
		return;	//?? 异常处理

	int n = 0;
	for (int i = 1; !(i & nPile); i <<= 1, n++);	// 把二进制位转换成数

	sendBitByNum(nPLCAdd, n, nValue);
}

/// <说明>发送一位二进制数据到PLC</说明>
void Socket::sendBitByNum(int nPLCAdd, int nNum, WORD nValue)
{
	ASSERT(nNum >= 0 && nNum <= 15);	// 16位

	m_pSendData[12] = 0x32;		// PLC HR段内存，产品点按二进制位操作，各数据定义参见F:\ProjectBackup\PLC\OMRON FIns_Tcp协议.zip\FINS通讯手册.pdf P120
	m_pSendData[16] = 0x00;		// 每次写一位，二进制位操作时按字节计算长度，每位一个字节。
	m_pSendData[17] = 0x01;		// 每次写一位，二进制位操作时按字节计算长度，每位一个字节。

	m_pSendData[13] = GETBYTE(nPLCAdd, 1);		// 13、14 起始地址PLC格式
	m_pSendData[14] = GETBYTE(nPLCAdd, 0);	// 地址
	m_pSendData[15] = (BYTE)nNum;	// 位地址
	m_pSendData[100] = (BYTE)nValue;	// 对应位置1

	if (!m_sendQueue.add(m_pSendData, 100))
	{
		// 保存日志
		//GException ge;
		//ge.m_nCode = 2038;
		//ge.m_strObject = m_strIPPort;
		//ge.m_strSummary = L"发送缓冲区满";

		//theApp.m_pLogList->addLog(&ge);

		onScoketError();
	}
}

/// <说明>发送多个数据到PLC</说明>
void Socket::sendData(int nPLCAdd, PWORD pData, int len)
{
	ASSERT((UINT_PTR)pData > 0xfffff);	// ?? 用于判断是不是指针，指针数值比较大。不严谨
	ASSERT(len <= 200);	// 20180817 江西唯美，最多16对 // 20200824 重庆唯美，从32改为64
	m_pSendData[12] = 0xB2;		// PLC HR段内存，产品点按字操作，各数据定义参见F:\ProjectBackup\PLC\OMRON FIns_Tcp协议.zip\FINS通讯手册.pdf P120
	m_pSendData[13] = GETBYTE(nPLCAdd, 1);	// 13、14 起始地址PLC格式
	m_pSendData[14] = GETBYTE(nPLCAdd, 0);	// HR段起始地址 从H9开始
	m_pSendData[15] = 0;	// 位地址，按二进制位发送有效，按字发送时为0
	m_pSendData[16] = GETBYTE(len, 1);		// 16、17 写数据数量，PLC格式
	m_pSendData[17] = GETBYTE(len, 0);

	//memcpy( m_pSendData + HEADNUM, pData, len * 2 );

	// 存储收到的数据到模拟PLC中
	PBYTE p1 = PBYTE(m_pSendData) + 100;
	PBYTE p2 = PBYTE(pData);
	int nn = len * 2;
	// PLC格式，每个字的高低字节交换
	// ???? 有没有线程同步问题，要不要加销
	for (int i = 0; i < nn; i += 2)
		p1[i] = p2[i + 1];
	for (int i = 1; i < nn; i += 2)
		p1[i] = p2[i - 1];

	if (!m_sendQueue.add(m_pSendData, 100))
	{
		// 保存日志
		//GException ge;
		//ge.m_nCode = 2038;
		//ge.m_strObject = m_strIPPort;
		//ge.m_strSummary = L"发送缓冲区满";

		//theApp.m_pLogList->addLog(&ge);

		onScoketError();
	}
}















// 构造队列
SendQueue::SendQueue()
	: nCount(0)
{
}

// 添加一行数据到尾部, PBYTE pData [in] 要添加的数据, int len [in] 数据长度，确保与队列数据长度一致。
// 成功返回TRUE，已满返回FALSE
BOOL SendQueue::add( PBYTE pData, int len )
{
	//ASSERT( len == TOTALSEND );
	//ASSERT( *PDWORD(pData) == 0x534e4946 );	// 数据开头是FINS

	//if( nCount == SENDQUEUESIZE )
	//	return FALSE;

	criticalBuf.Lock();	//线程互斥，多个线程操作queBuffer时，只有一个线程工作。

	memcpy( queBuffer[nCount++], pData, len );	// 把数据添加到队尾，计数加1

	criticalBuf.Unlock();	//解锁，允许其它线程工作。

	return TRUE;
}

// 删除并返回数组的第一个元素, PBYTE pData [out] 返回的数据, int len [in] 数据长度，确保与队列数据长度一致。
// 成功返回TRUE，已满返回FALSE
/*BOOL SendQueue::shift( PBYTE pData, int len )	// 删除并返回数组的第一个元素
{
	ASSERT( len == TOTALSEND );
	ASSERT(nCount >= 0);

	if( nCount == 0 )
		return FALSE;

	mutexBuf.Lock();	//线程互斥，多个线程操作queBuffer时，只有一个线程工作。

	memcpy( pData, queBuffer[0], len );			// 复制第一条数据，计数-1
	--nCount;
	memcpy( queBuffer[0], queBuffer[1], nCount * TOTALSEND );	// 队列前移。

	mutexBuf.Unlock();	//解锁，允许其它线程工作。

	//ASSERT( *PDWORD(pData) == 0x534e4946 );	// 数据开头是FINS

	return TRUE;
}
*/

/// <作者>Mr. Hu</作者>
/// <日期>20180618</日期>
/// <名称>getFirst</名称>
/// <功能>取发送队列的第一条数据，原数据保留</功能>
/// <参数 name = "PBYTE pData">接收缓存</参数>
/// <参数 name = "int len">接收长度</参数>
/// <返回>没有数据时返回FALSE，否则返回TREU</返回>
/// <说明>取发送队列的第一条数据，原数据保留</说明>
BOOL SendQueue::getFirst(PBYTE pData, int len)
{
	//ASSERT(len == TOTALSEND);
	ASSERT(nCount >= 0);

	if (nCount == 0)
		return FALSE;

	criticalBuf.Lock();	//线程互斥，多个线程操作queBuffer时，只有一个线程工作。

	memcpy(pData, queBuffer[0], len);			// 复制第一条数据，计数-1

	criticalBuf.Unlock();	//解锁，允许其它线程工作。

	//ASSERT( *PDWORD(pData) == 0x534e4946 );	// 数据开头是FINS

	return TRUE;
}

/// <作者>Mr. Hu</作者>
/// <日期>20180618</日期>
/// <名称>removeFirst</名称>
/// <功能>删除发送队列的第一条数据</功能>
/// <参数 name = ""></参数>
/// <返回>没有数据时返回FALSE，否则返回TRUE</返回>
/// <说明>删除发送队列的第一条数据</说明>
BOOL SendQueue::removeFirst()
{
	//ASSERT(len == TOTALSEND);
	ASSERT(nCount >= 0);

	if (nCount == 0)
		return FALSE;

	criticalBuf.Lock();	//线程互斥，多个线程操作queBuffer时，只有一个线程工作。

	--nCount;
	memcpy(queBuffer[0], queBuffer[1], nCount * 100);	// 队列前移。

	criticalBuf.Unlock();	//解锁，允许其它线程工作。

	//ASSERT( *PDWORD(pData) == 0x534e4946 );	// 数据开头是FINS

	return TRUE;
}

// 清除全部
void SendQueue::clear()
{
	nCount = 0;
}

// 20180415 加下面方法，发送数据时先不移除，确认发送成功后再移除。	// 发送单条指令时也有这个问题。
// 取第一条数据，成功返回TRUE，否则返回FALSE
/*	// 20180418 再想办法
BOOL SendQueue::getFirst(PBYTE pData, int len)
{
	ASSERT(len == TOTALSEND);
	ASSERT(nCount >= 0);

	if (nCount == 0)
		return FALSE;

	mutexBuf.Lock();	//线程互斥，多个线程操作queBuffer时，只有一个线程工作。

	memcpy(pData, queBuffer[0], len);			// 复制第一条数据，计数-1

	mutexBuf.Unlock();	//解锁，允许其它线程工作。

						//ASSERT( *PDWORD(pData) == 0x534e4946 );	// 数据开头是FINS

	return TRUE;
}

// 移除第一条数据
void SendQueue::removeFirst()
{
	ASSERT(nCount >= 0);

	if (nCount == 0)
		return;

	mutexBuf.Lock();	//线程互斥，多个线程操作queBuffer时，只有一个线程工作。

	--nCount;
	memcpy(queBuffer[0], queBuffer[1], nCount * TOTALSEND);	// 队列前移。

	mutexBuf.Unlock();	//解锁，允许其它线程工作。

	//ASSERT( *PDWORD(pData) == 0x534e4946 );	// 数据开头是FINS
}
*/

